class BaseParent:
    """Represents a Weverse Base Parent class.

    Inherited by:

    - :class:`objects.comment.ParentComment`
    - :class:`objects.comment.ParentPost`
    - :class:`objects.comment.ParentMediaPost`

    Attributes
    ----------
    data: :class:`str`
        The raw data directly taken from the response generated by Weverse's API.
    body: :class:`str`
        The body of the base parent object.
    author: :class:`objects.comment.BaseAuthor`
        The :class:`objects.comment.BaseAuthor` object of the parent author
        who wrote the base parent object.
    """

    __slots__ = ("data", "body", "author")

    def __init__(self, data: dict):
        self.data: dict = data
        self.body: str = data["body"]
        self.author: BaseAuthor = BaseAuthor(data["author"])


class ParentComment(BaseParent):
    """Represents a Weverse Parent Comment. Inherits from
    :class:`objects.comment.BaseParent`.

    Shares the same attributes with :class:`objects.comment.BaseParent`.

    .. container:: operations

        .. describe:: x == y

            Checks if two parent comments are equal.

        .. describe:: x != y

            Checks if two parent comments are not equal.

        .. describe:: hash(x)

            Returns the parent comment's hash.

        .. describe:: str(x)

            Returns the parent comment's body.

    Attributes
    ----------
    id: :class:`str`
        The ID of the parent comment. If more information about the parent
        comment is required by the user, the :class:`SunverseClient.fetch_comment()`
        method can be used to fetch the actual :class:`objects.comment.Comment`
        object.
    created_at: :class:`int`
        The time the parent comment got created at, in epoch.
    """

    __slots__ = ("id", "created_at")

    def __init__(self, data: dict):
        super().__init__(data)
        self.id: str = data["commentId"]
        self.created_at: int = data["createdAt"]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Parent Comment comment_id={self.id}, body={self.body}"

    def __str__(self):
        return self.body

    def __hash__(self):
        return hash(self.id)


class ParentPost(BaseParent):
    """Represents a Weverse Parent Post. Inherits from
    :class:`objects.comment.BaseParent`.

    Shares the same attributes with :class:`objects.comment.BaseParent`.

    Inherited by:

    - :class:`objects.comment.ParentMediaPost`
    - :class:`objects.comment.RootPost`

    .. container:: operations

        .. describe:: x == y

            Checks if two parent posts are equal.

        .. describe:: x != y

            Checks if two parent posts are not equal.

        .. describe:: hash(x)

            Returns the parent post's hash.

        .. describe:: str(x)

            Returns the parent post's plain body.

    Attributes
    ----------
    id: :class:`str`
        The ID of the parent post. If more information about the parent
        post is required by the user, the :class:`Sunverse.Client.fetch_post()`
        method can be used to fetch the actual :class:`objects.post.Post` object.
    plain_body: :class:`str`
        The plain body of the parent post that does not have markdowns
        and unnecessary information.
    post_type: :class:`str`
        The post type of the parent post.
    """

    __slots__ = ("id", "plain_body", "post_type")

    def __init__(self, data: dict):
        super().__init__(data)
        self.id: str = data["postId"]
        self.plain_body: str = data["plainBody"]
        self.post_type: str = data["postType"]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Parent Post post_id={self.id}, body={self.plain_body}"

    def __str__(self):
        return self.plain_body

    def __hash__(self):
        return hash(self.id)


class ParentMediaPost(ParentPost):
    """Represents a Weverse Parent Media Post. Inherits from
    :class:`objects.comment.BaseParent` and :class:`objects.comment.ParentPost`.

    Shares the same attributes with :class:`objects.comment.BaseParent` and
    :class:`objects.comment.ParentPost`.

    .. container:: operations

        .. describe:: str(x)

            Returns the parent media post's title.

    Attributes
    ----------
    title: :class:`str`
        The title of the parent media post.
    """

    __slots__ = ("title",)

    def __init__(self, data: dict):
        super().__init__(data)
        self.title: str = data["title"]

    def __repr__(self):
        return f"Parent Media Post post_id={self.id}, title={self.title}"

    def __str__(self):
        return self.title


class RootPost(ParentPost):
    """Represents a Weverse Root Post. Inherits from
    :class:`objects.comment.BaseParent` and :class:`objects.comment.ParentPost`.

    Shares the same attributes with :class:`objects.comment.BaseParent` and
    :class:`objects.comment.ParentPost`.

    Attributes
    ----------
    url: :class:`str`
        The URL that leads to the root post.
    section_type: :class:`str`
        The section the root post falls under.
    author: :class:`objects.comment.Author`
        The :class:`objects.comment.Author` object of the author
        who wrote the root post.
    """

    __slots__ = ("url", "section_type")

    def __init__(self, data: dict):
        super().__init__(data)
        self.url: str = data["shareUrl"]
        self.section_type: str = data["sectionType"]
        self.author: Author = Author(data["author"])

    def __repr__(self):
        return f"Root Post post_id={self.id}, body={self.plain_body}"


class RootMediaPost(ParentMediaPost):
    """Represents a Weverse Root Media Post. Inherits from
    :class:`objects.comment.BaseParent`, :class:`objects.comment.ParentPost`
    and :class:`objects.comment.ParentMediaPost`.

    Shares the same attributes with :class:`objects.comment.BaseParent`,
    :class:`objects.comment.ParentPost` and :class:`objects.comment.ParentMediaPost`.

    Attributes
    ----------
    url: :class:`str`
        The URL that leads to the root media post.
    section_type: :class:`str`
        The section the root media post falls under.
    author: :class:`objects.comment.Author`
        The :class:`objects.comment.Author` object of the author
        who wrote the root media post.
    """

    __slots__ = ("url", "section_type")

    def __init__(self, data: dict):
        super().__init__(data)
        self.url: str = data["shareUrl"]
        self.section_type: str = data["sectionType"]
        self.author: Author = Author(data["author"])

    def __repr__(self):
        return f"Root Media Post post_id={self.id}, title={self.title}"


class Comment:
    """Represents a Weverse Comment.

    .. container:: operations

        .. describe:: x == y

            Checks if two comments are equal.

        .. describe:: x != y

            Checks if two comments are not equal.

        .. describe:: hash(x)

            Returns the comment's hash.

        .. describe:: str(x)

            Returns the comment's body.

    Attributes
    ----------
    data: :class:`str`
        The raw data directly taken from the response generated by Weverse's API.
    id: :class:`str`
        The ID of the comment.
    body: :class:`str`
        The body of the comment.
    comment_count: :class:`int`
        The number of comments for the comment.
    like_count: :class:`int`
        The number of likes for the comment.
    created_at: :class:`int`
        The time the comment got created at, in epoch.
    author: :class:`objects.comment.Author`
        The :class:`objects.comment.Author` object of the author
        who wrote the comment.
    """

    __slots__ = (
        "data",
        "id",
        "body",
        "comment_count",
        "like_count",
        "created_at",
        "author",
    )

    def __init__(self, data: dict):
        self.data: dict = data
        self.id: str = data["commentId"]
        self.body: str = data["body"]
        self.comment_count: int = data["commentCount"]
        self.like_count: int = data["emotionCount"]
        self.created_at: int = data["createdAt"]
        self.author: Author = Author(data["author"])

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Comment comment_id={self.id}, body={self.body}"

    def __str__(self):
        return self.body

    def __hash__(self):
        return hash(self.id)

    @property
    def parent(self) -> ParentComment | ParentMediaPost | ParentPost:
        """:class:`objects.comment.ParentComment` |
        :class:`objects.comment.ParentPost` |
        :class:`objects.comment.ParentMediaPost`:

        - Returns the :class:`objects.comment.ParentComment` object if the\
        parent is a comment.

        - Returns the :class:`objects.comment.ParentMediaPost` object if the\
        parent is a media post.

        - Returns the :class:`objects.comment.ParentPost` object if the parent\
        is a post.
        """
        post_types = ["NORMAL", "MOMENT", "OFFICIAL"]

        if self.data["parent"]["type"] == "COMMENT":
            return ParentComment(self.data["parent"]["data"])

        if self.data["parent"]["type"] == "POST":
            if self.data["parent"]["data"]["postType"] in post_types:
                return ParentPost(self.data["parent"]["data"])

            else:
                return ParentMediaPost(self.data["parent"]["data"])

    @property
    def root(self) -> RootMediaPost | RootPost:
        """:class:`objects.comment.RootMediaPost` |
        :class:`objects.comment.RootPost`:

        - Returns the :class:`objects.comment.RootMediaPost` object if the\
        root post is a media post.

        - Returns the :class:`objects.comment.RootPost` object if the root\
        post is a normal post.
        """
        post_types = ["NORMAL", "MOMENT", "OFFICIAL"]

        if self.data["root"]["data"]["postType"] in post_types:
            return RootPost(self.data["root"]["data"])

        else:
            return RootMediaPost(self.data["root"]["data"])

    @property
    def url(self) -> str:
        """:class:`str`: Returns the URL to the comment."""
        return self.root.url + f"/comment/{self.id}"


class BaseAuthor:
    """Represents a Weverse Base Author class.

    Inherited by:

    - :class:`objects.comment.Author`

    .. container:: operations

        .. describe:: x == y

            Checks if two base authors are equal.

        .. describe:: x != y

            Checks if two base authors are not equal.

        .. describe:: hash(x)

            Returns the base author's hash.

        .. describe:: str(x)

            Returns the base author's name.

    Attributes
    ----------
    data: :class:`str`
        The raw data directly taken from the response generated by Weverse's API.
    id: :class:`str`
        The ID of the base author. If more information about the author is
        required by the user, the :class:`SunverseClient.fetch_member()` method
        can be used to fetch the actual :class:`objects.member.Member` object.
    name: :class:`str`
        The name of the base author.
    image_url: :class:`str`
        The URL of the profile image of the base author. Returns an empty
        string if the base author does not have a profile image.
    profile_type: :class:`str`
        The profile type of the base author.
    """

    __slots__ = (
        "data",
        "id",
        "name",
        "image_url",
        "profile_type",
    )

    def __init__(self, data: dict):
        self.data: dict = data
        self.id: str = data["memberId"]
        self.name: str = data["profileName"]
        self.image_url: str = (
            data["profileImageUrl"] if data.get("profileImageUrl") else ""
        )
        self.profile_type: str = data["profileType"]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Parent Author author_id={self.id}, name={self.name}"

    def __str__(self):
        return self.name

    def __hash__(self):
        return hash(self.id)


class Author(BaseAuthor):
    """Represents a Weverse Author. Inherits from :class:`objects.comment.BaseAuthor`.

    Shares the same attributes with :class:`objects.comment.BaseAuthor`.

    Attributes
    ----------
    has_official_mark: :class:`bool`
        Whether the author is an official Weverse account.
    community_id: :class:`int`
        The community ID of the community the author belongs to. This
        can be used to fetch the actual :class:`objects.community.Community`
        object if needed using the :class:`SunverseClient.fetch_community()`
        method.
    """

    __slots__ = ("has_offical_mark", "community_id")

    def __init__(self, data: dict):
        super().__init__(data)
        self.has_offical_mark: bool = data["hasOfficialMark"]
        self.community_id: int = data["communityId"]

    def __repr__(self):
        return f"Author author_id={self.id}, name={self.name}"
